/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
/**
 * Software License, Version 1.0
 *
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 *
 *
 *Redistribution and use in source and binary forms, with or without
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For
 * further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and
 * wherever such third-party acknowledgments normally appear.
 *
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering,
 * shall not be used to endorse or promote products derived from this software
 * without prior written permission from Indiana University.  For written
 * permission, please contact the Advanced Research and Technology Institute
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering,
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 *
 *
 * Indiana University provides no reassurances that the source code provided
 * does not infringe the patent or any other intellectual property rights of
 * any other entity.  Indiana University disclaims any liability to any
 * recipient for claims brought by any other entity based on infringement of
 * intellectual property rights or otherwise.
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.transport.ptcp;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;

import cgl.narada.protocol.Protocol;
import cgl.narada.transport.TransportDebugFlags;
import cgl.narada.transport.TransportHandler;
import cgl.narada.transport.ptcp.psocket.PTCPInputStream;
import cgl.narada.transport.ptcp.psocket.PTCPSocket;

/**
   Listens for TCP packets arriving at a socket and performs various operations
   based on situations arising during communication. These received packets
   are then encapsulated into a specialized data structure and forwarded to the
   transport handler implenetation residing in the transport layer.

   @author Shrideep Pallickara
   $Revision$
   $Date$
 */

public class PTCPReceiverThread
    extends Thread
    implements TransportDebugFlags {

  private Socket socket;
  private PTCPSocket psocket;
  private DataInputStream din;
  private PTCPInputStream pis;
  private BufferedInputStream bin;
  private byte[] data;

  private int packetTracker = 0;
  private boolean zeroLengthRead = false;
  private long readingDelayStart;

  private PTCPLink ptcpLink;

  private String moduleName = "PTCPReceiverThread: ";
  private TransportHandler transportHandler;
  private String linkId;
  private static FileOutputStream myfile = null;

//Remove this constructor later
  /*
     public PTCPReceiverThread(Socket socket) {
    initializeDataInputStream(socket);
     }
   */
  public PTCPReceiverThread(PTCPSocket psocket) {
    initializeDataInputStream(psocket);
  }

//Remove this constructor later
  /*
     public PTCPReceiverThread(Socket socket, PTCPLink ptcpLink) {
    this.ptcpLink = ptcpLink;
    initializeDataInputStream(socket);
    transportHandler = ptcpLink.getTransportHandler();
    if (transportHandler == null ) {
      System.out.println(moduleName + "TransportHandler is null");
    }
     }
   */
  public PTCPReceiverThread(PTCPSocket psocket, PTCPLink ptcpLink) {
    /* try {
       myfile = new FileOutputStream(System.currentTimeMillis() +
                                     "my-output.rar");
     }
     catch (FileNotFoundException ex) {
       ex.printStackTrace();
     }*/
    this.ptcpLink = ptcpLink;
    initializeDataInputStream(psocket);
    transportHandler = ptcpLink.getTransportHandler();
    if (transportHandler == null) {
      System.out.println(moduleName + "TransportHandler is null");
    }
  }

  private void initializeDataInputStream(PTCPSocket psocket) {
    this.psocket = psocket;
    if (this.psocket == null) {
      System.out.println(moduleName + " ERROR: psocket is null");
    }
    try {
      if (!psocket.getTcpNoDelay()) {
        if (PTCPReceiverThread_Debug) {
          System.out.println(moduleName + "Nagle's algorithm being disabled");
        }
        psocket.setTcpNoDelay(true);
      }
    }
    catch (SocketException se) {
      se.printStackTrace();
    }

    try {
      pis = (PTCPInputStream) psocket.getInputStream();
    }
    catch (IOException ioe) {
      ioe.printStackTrace();
    }
  }

  /*
    private void initializeDataInputStream(Socket socket) {
      this.socket = socket;
      try {
        if (!socket.getTcpNoDelay()) {
   if (TCPReceiverThread_Debug)
     System.out.println(moduleName + "Nagle's algorithm being disabled");
   socket.setTcpNoDelay(true);
        }
      } catch (SocketException se) {
        se.printStackTrace();
      }

      try {
        din = new DataInputStream(socket.getInputStream());
      } catch (IOException ioe) {
        ioe.printStackTrace();
      }
    }
   */
  public void setLinkId(String linkId) {
    if (linkId == null) {
      System.out.println(moduleName + "Specified Link ID is null");
    }
    this.linkId = linkId;
  }

  public void releaseResources() {
    if (PTCPReceiverThread_Debug) {
      System.out.println(moduleName + "Closing " + psocket);
    }
    try {
      psocket.getInputStream().close();
      psocket.close();
    }
    catch (IOException ioe) {
      System.out.println(moduleName + "Problem while releasing resources" +
                         ioe);
    }
  }

  public void run() {
    if (PTCPReceiverThread_Debug) {
      System.out.println("PTCPReceiverThread listening for messages to " +
                         psocket);
    }
    int dataLength;
    int packetTracker = 0;
    byte transportHeader;
    while (psocket != null) {
      try {
        if (PTCPReceiverThread_Debug) {
          System.out.println("Receiving ..");

        }
        byte[] data = null;

        try {
          data = pis.receive();
        }
        catch (Exception ex) {
          System.out.println(moduleName + ex);
          break;
        }

        ByteArrayInputStream baiStream = new ByteArrayInputStream(data);
        DataInputStream inputStream = new DataInputStream(baiStream);

        transportHeader = inputStream.readByte();

        if (transportHeader == Protocol.PING) {
          System.out.println("Received ping ");
          //senderThread.sendPingResponse(din.readLong());
          continue;
        }

        if (transportHeader == Protocol.PING_REPLY) {
          long timeSent = din.readLong();
          System.out.println("Ping time = " +
                             (System.currentTimeMillis() - timeSent));
          continue;
        }

        if (transportHeader != Protocol.DATA) {
          System.out.println("\n \n" + moduleName +
                             "MAJOR ERROR: Received Data with garbled " +
                             "transport header" + "\n \n");
          continue;
        }

        dataLength = inputStream.readInt();
        if (PTCPReceiverThread_Debug && dataLength > 100000) {
          System.out.println(moduleName + "Flaggin data of size[" +
                             dataLength + "] on link " + linkId);
        }
        if (PTCPReceiverThread_Debug) {
          System.out.println(moduleName + "Reading data of size [" +
                             dataLength + "] on link " + linkId);
        }
        data = new byte[dataLength];
        inputStream.readFully(data, 0, dataLength);
        //myfile.write(data);
        packetTracker++;
        if (PTCPReceiverThread_Debug) {
          System.out.println(moduleName + "Read data of size [" +
                             dataLength + "] on link " + linkId);
          System.out.println(moduleName + "Packet Number=" + packetTracker +
                             " , length= " + dataLength);
        }

        transportHandler.dataReceived(data, linkId);

      }
      catch (SocketException se) {
        System.out.println(moduleName + se);
        break;
      }
      catch (IOException ioe) {
        System.out.println(moduleName + ioe);
        break;
      }
    }

    /** Report loss of connection to the PTCP Link */
    ptcpLink.processLossOfSocketConnection(false);

    if (TCPReceiverThread_Debug) {
      System.out.println("PTCPReceiver: Socket Connection to [" + psocket +
                         "] broken");

    }
  }
}
